package cloudspace.ui.applet;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import student.web.SharedPersistentMap;
import student.web.internal.LocalApplicationSupportStrategy;
import student.web.internal.PersistentStorageManager;
import student.web.internal.PersistentStorageManager.StoredObject;
import student.web.internal.Snapshot;
import student.web.internal.converters.Alias;

import cloudspace.ui.applet.io.CloudFile;
import cloudspace.ui.applet.io.CommandHandler;
import cloudspace.vm.VM;

import com.Ostermiller.util.Base64;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;


public class AppletApplicationSupportStrategy
                extends
                LocalApplicationSupportStrategy
{
    public ReflectionProvider getReflectionProvider()
    {
        return new AppletReflectionProvider();
    }


    public File getPersistentBase()
    {
        return new CloudFile( "WEB-INF/datastore", true );
    }


    public File getPersistentFile( String subDir )
    {
        return new CloudFile( subDir, true );
    }


    public File getPersistentFile( File baseDir, String subDir )
    {
        return new CloudFile( baseDir.getPath(), subDir, true );
    }


    private static class ObjectSourceHandler implements CommandHandler
    {

        public CloudToApplet handleRequest(
            Map<String, Object> params,
            Map<String, Object> objects ) throws Exception
        {
            String path = (String)params.get( "path" );
            PersistentStorageManager psm = PersistentStorageManager.getInstance( "shared" );
            String result = "";
            psm.lock();
            try
            {
                BufferedReader read = new BufferedReader( new FileReader( path ) );
                String line = read.readLine();
                while ( line != null )
                {
                    result += line;
                    line = read.readLine();

                }
                read.close();
            }
            finally
            {
                psm.unlock();
            }

            return new CloudToApplet( result );
        }

    }

    private static final String READ_JS_FUNC = "readObjectSource";

    private static final String READ_EVENT = "onReadObjectSource";

    private static final String[] READ_PARAM = new String[] { "path" };

    private static String READ_FUNC = JSFactory.generateJSFunction( READ_JS_FUNC,
        READ_EVENT,
        READ_PARAM );


    public InputStream getObjectSource( File src )
    {
        CloudResult result = AppletVM.callJScriptFunc( READ_JS_FUNC,
            src.getAbsolutePath() );
        if ( result.isError() )
        {
            Throwable throwing = result.getException();
            throw new RuntimeException( throwing );
        }
        String response = result.getResult( 0 );
        return new ByteArrayInputStream( response.getBytes() );
    }


    private static class ObjectOutputHandler implements CommandHandler
    {

        public CloudToApplet handleRequest(
            Map<String, Object> params,
            Map<String, Object> objects ) throws Exception
        {
            // String reqId = (String)params.get( "reqId" );
            // String localId = (String)params.get( "id" );
            String path = (String)params.get( "path" );
            String data = (String)params.get( "xml" );
            String decoded = Base64.decode( data );
            PersistentStorageManager psm = PersistentStorageManager.getInstance( "shared" );
            String result = "";
            psm.lock();
            try
            {
//                 String id = psm.unsanitizeId( path.substring(
//                 path.lastIndexOf( '/' )+1, path.lastIndexOf( '.' ) ));
//                 Snapshot meta = new Snapshot();
//                 Object value = psm.readObjectFromXML( new
//                 ByteArrayInputStream(decoded.getBytes()),
//                 VM.currentVM().getLoader(), meta );
//                 StoredObject sObject = psm.getPersistentObject( id,
//                 VM.currentVM().getLoader() );
//                 // sObject.setFieldset( meta );
//                 // sObject.setValue( value );
//                 // sObject.setValue( value );
//                 psm.storePersistentObjectChanges( id, new
//                 StoredObject(sObject.id(), sObject.sanitizedId(), value,
//                 meta, 0), VM.currentVM().getLoader() );
                FileWriter writer = new FileWriter( path );
                writer.write( decoded );
                writer.close();
            }
            finally
            {
                psm.unlock();
            }

            return new CloudToApplet( result );
        }

    }

    private static final String WRITE_JS_FUNC = "writeObjectSource";

    private static final String WRITE_EVENT = "onWriteObjectSource";

    private static final String[] WRITE_PARAM = new String[] { "path", "xml" };

    private static String WRITE_FUNC = JSFactory.generateJSFunction( WRITE_JS_FUNC,
        WRITE_EVENT,
        WRITE_PARAM );


    public OutputStream getObjectOutput( final File dest )
    {
        return new ByteArrayOutputStream()
        {
            @Override
            public void close() throws IOException
            {
                super.close();
                String encoded = BASE64.encodeContent( this.toByteArray() );
                String path = dest.getAbsolutePath();
                CloudResult result = AppletVM.callJScriptFunc( WRITE_JS_FUNC,
                    path,
                    encoded );
                if ( result.isError() )
                {
                    Throwable throwing = result.getException();
                    if ( throwing instanceof IOException )
                        throw (IOException)throwing;
                    throw new RuntimeException( throwing );
                }
            }
        };
    }


    public Object getAlias( Object value )
    {
        // I never alias (CloudSpace will do it for me)
        return value;
    }


    public Object resolveAlias( Object value )
    {
        // I must be able to unalias because cloudspace will send me alias
        // entries
        if ( value != null && value instanceof Alias )
        {
            Snapshot preserveLocal = Snapshot.getLocal();
            SharedPersistentMap<Object> pMap = new SharedPersistentMap<Object>( Object.class );
            Object resolved = pMap.get( ( (Alias)value ).getKey() );
            Snapshot.setLocal( preserveLocal );
            return resolved;
        }
        return value;
    }

    private static final List<String> localScripts = new ArrayList<String>();

    static
    {
        localScripts.add( READ_FUNC );
        localScripts.add( WRITE_FUNC );

    }


    public static List<String> getJavaScript()
    {
        return localScripts;
    }


    public static Map<String, CommandHandler> getCommandHandlers()
    {
        Map<String, CommandHandler> localEvents = new HashMap<String, CommandHandler>();
        localEvents.put( READ_EVENT, new ObjectSourceHandler() );
        localEvents.put( WRITE_EVENT, new ObjectOutputHandler() );
        return localEvents;
    }


    @Override
    public Map<String, Object> getSessionPersistentMap()
    {
        return new AppletDistributedMap<String, Object>();
    }
}
